/******************************************************************************
 *  _____     _____
 *  \  __\    \  __\
 *   \ \_\\    \ \_\\
 *    \  __\SS  \  __\roject
 *     \_\       \_\
 *
 * Mateusz Maciąg, Rafał Urbański
 * Komputerowe Systemy Sterowania
 *****************************************************************************/

#ifndef ISIMULATOR_H
#define ISIMULATOR_H

#include <memory>
#include "IConfigurable.h"
#include "IDiscreteObject.h"

#include <boost/smart_ptr.hpp>

namespace RafMat
{
namespace SimInterface
{

// Deklaracje zapowiadająće
class ISimulator;

/** Alias na typ zręcznego wskaźnika na obiekt ISimulator. */
typedef boost::shared_ptr<ISimulator> ISimulatorSPtr;

/**
    Interfejs symulatora.

    Interfejs wykorzystywany jest przez GUI, a implementowany przez symulator.
    GUI wywołuje metody typu doStep, nie znając właściwie ich implementacji.
    W ten sposób oddzielona jest całkowicie implementacja symulatora od
    implementacji interfejsu użytkownika.

    Ponadto GUI może pobierać aktualną wartość na wejściu zadawanym,
    wartość wypracowaną przez regulator oraz wyjście układu regulacji.
    Gui ma także możliwość tworzenia obiektów ARX oraz ich usuwania.

    Przez ten interfejs modyfikujemy także regulator, oraz zestaw sygnałów
    oznaczających wartości zadane.

    Utworzenie instancji ISimulator możliwy jest przez klasę SimulatorManager.
*/
class ISimulator
{
public:

    /** Typ regulatora */
    enum ControllerType
    {
        ControllerTypeOpenLoop,     ///< Pseudoregulator typu "otwarta pętla sprzężenia zwrotnego".
        ControllerTypeGain,         ///< Regulator typu P.
        ControllerTypePID,          ///< Regulator PID.
        ControllerTypeGPC,          ///< Regulator predykcyjny GPC.
        ControllerTypeWRMNKTest,    ///< Regulator testowy dla metody RMNK (działa jak OpenLoop).
        ControllerTypeRST           ///< Regulator RST z algorytmami adaptacyjnymi.
    };


    /** Typ algorytmu adaptacyjnego*/
    enum AdptType
    {
        AdptTypeStatic,             ///< Alg. z ręcznie ustawianymi wielomianami
        AdptTypeAPP,
        AdptTypeAPZP1
    };

    /** Typ wejścia */
    enum InputType
    {
        InputTypeSquare,            ///< Sygnał prostokątny.
        InputTypeSine,              ///< Sygnał sinusoidalny.
        InputTypeTriangle,          ///< Sygnał trójkątny.
        InputTypeStep,              ///< Skok wartości.
        InputTypeKronecker,         ///< Impuls Kroneckera.
        InputTypeRandomNoise,       ///< Szum losowy o rozkładzie jednostajnym.
        InputTypeSweep,             ///< Przemiatanie zakresu częstotliwości
        InputTypeKuba,
        InputTypeCustom             ///< Sygnał użytkownika.
    };

    /** Typ nieliniowości */
    enum NonlinearityType
    {
        NonlinearityNone,
        NonlinearityNLSaturation
    };

    typedef std::map<std::string, ControllerType> ControllerMap;
    typedef std::map<std::string, InputType> InputMap;
    typedef std::map<std::string, NonlinearityType> NLMap;

    /**
        Wykonuje jeden cykl obliczeń uaktualniając wszystkie obiekty w symulatorze.
    */
    virtual void doStep() = 0;

    /**
        Resetuje symulator do stanu początkowego (próbka 0).
    */
    virtual void reset() = 0;

    /**
        Zwraca numer aktualnej próbki.
    */
    virtual unsigned int currentSampleNum() const = 0;

    /**
        Zwraca aktualną wartość zadaną wprowadzoną do pętli sprzężenia zwrotnego.
        Wartość ta wynika ze zsumowania wszystkich sygnałów wejściowych.
    */
    virtual double getInputValue() const = 0;

    /**
        Zwraca aktualną wartość na wyjściu pętli regulacji.
    */
    virtual double getOutputValue() const = 0;

    /**
        Zwraca aktualną wartość na wyjściu regulatora.
    */
    virtual double getControllerOutput() const = 0;

    /**
        Tworzy nowy obiekt ARX typu wtórnik.
        Nowo stworzony obiekt przygotowany jest do obsługiwania modeli
        o rzędach ograniczanych przez podawane argumenty.

        Stworzony obiekt przechowywany jest w pojemniku stanowiącym łańcuch szeregowo
        połączonych obiektów.

        \param [in] aDegree ograniczenie górne na rząd mianownika transmitancji.
        \param [in] bDegree ograniczenie górne na rząd licznika transmitancji.
        \param [in] delay ograniczenie górne na opóźnienie obiektu.

        \return Zręczny wskaźnik na utworzony obiekt.
    */
    virtual IDiscreteObjectSPtr createObject(int aDegree, int bDegree, int cDegree, int agDegree, int d, int delay = 0) = 0;

    /**
        Zwraca wszystkie obiekty dynamiczne.
    */
    virtual std::vector<IDiscreteObjectSPtr> getObjects() const = 0;

    /**
        Zwraca wszystkie wejścia.
    */
    virtual std::vector<IConfigurableSPtr> getInputs() const = 0;

    /**
        Usuwa obiekt z łańcucha szeregowo połączonych obiektów.

        \param [in] obj Zręczny wskaźnik na obiekt, który chcemy usunąć.
    */
    virtual void removeObject(IDiscreteObjectSPtr obj) = 0;

    /**
        Zwraca konfigurację aktualnego regulatora.
    */
    virtual IConfigurableSPtr getController() = 0;

    /**
        Usuwa regulator z pętli sprzężenia i zastępuje go nowym.

        \param [in] ctrlType Typ regulatora.
        \param [in] copuInputs Flaga kopiowania wejść. True - jeśli wejścia mają zostać zachowane.
        \return Obiekt konfiguracyjny tworzonego regulatora. */
    virtual IConfigurableSPtr createController(ControllerType ctrlType = ControllerTypeOpenLoop, bool copyInputs = true) = 0;

    /**
        Dodaje wejście do aktualnego regulatora.

        \param [in] inputTupe Type wejścia.
        \return Obiekt konfiguracyjny dodanego wejścia.
    */
    virtual IConfigurableSPtr addInput(InputType inputType) = 0;

    /**
        Usuwa wejście z aktualnego regulatora.
        Wejście do usunięcia jest rozpoznawalne przez obiekt konfiguracyjny.
    */
    virtual void removeInput(IConfigurableSPtr) = 0;


    virtual IConfigurableSPtr setNonlinearity(NonlinearityType nlType) = 0;

    virtual ControllerMap getControllerMap() const = 0;

    virtual InputMap getInputMap() const = 0;

    virtual NLMap getNonlinearityMap() const = 0;

    virtual IConfigurableSPtr getNonlinearityObj() const = 0;

    virtual IConfigurableSPtr getExternalPlant() const = 0;
    virtual IConfigurableSPtr enableExternalPlant(bool enable) = 0;

    virtual void enableDataCapture(bool enable) = 0;

    virtual void setCaptureFilePath(const std::string& path) = 0;

    /**
        Zapisuje całą konfigurację symulatora do pliku.

        \param [in] filePath Ścieżka do pliku docelowego.
    */
    virtual void saveConfiguration(std::string filePath) = 0;

    virtual void loadConfiguration(std::string filePath) = 0;

    static IConfigurableSPtr iDiscreteObjectToConfigurable(IDiscreteObjectSPtr obj);
    static IDiscreteObjectSPtr iConfigurableToDiscreteObject(IConfigurableSPtr obj);
};

}   // SimInterface namespace
}   // RafMat namespace


#endif // ISIMULATOR_H
